#ifndef RBT_URL_INCLUDE
#define RBT_URL_INCLUDE

#include "typedef.h"
#include "Text/Convert.h"
#include "Text/Encoding.h"
#include <algorithm>

RETURNCODE E_URL_ENCODING = -624000;
RETURNCODE E_URL_PROTOCOL = -624001;
RETURNCODE E_URL_SERVER   = -624002;
RETURNCODE E_URL_PORT     = -624003;
RETURNCODE E_URL_INVALID  = -624005;

struct HTTP_URL
{
	enum Protocol
	{
		HTTP,
		HTTPS
	};

	HTTP_URL()
	{

	}

	HTTP_URL( const HTTP_URL& url ) : 
		m_server( url.m_server ) , 
		m_argument( url.m_argument ) ,
		m_object( url.m_object ) , 
		m_port( url.m_port ) , 
		m_protocol( url.m_protocol ) 
	{

	}

protected:
	string    m_server;
	uint32    m_port;
	Protocol  m_protocol;
	string    m_object;
	string    m_argument;
public:
	string GetObject() const
	{
		if(m_argument.empty())
			return m_object;
		return m_object + _RBT("?") + m_argument;
	}

	uint32 GetPort() const
	{
		return m_port;
	}

	Protocol GetProtocol() const
	{
		return m_protocol;
	}

	string GetServer() const
	{
		return m_server;
	}

};

class HttpUrl :
	public HTTP_URL
{

	Encoding* m_encoding;

	int32 MappingProtocol( cstring& value , Protocol& protocol )
	{
		if( value == _RBT("HTTP") )
			protocol = HTTP;
		else if( value == _RBT("HTTPS") )
			protocol = HTTPS;
		else
			return E_URL_PROTOCOL;
		return R_SUCCEED;
	}

	int32 ParseUrl( cstring& value )
	{
		string url = value;

		typedef string::size_type pos_t;

		int32 code = R_SUCCEED;

		for( pos_t idx = 0 ; idx < url.size() ; ++idx )
			url[idx] = toupper(url[idx]);

		pos_t npos = string::npos;

		pos_t pos = url.find(_RBT("://"));

		string protcol;
		if( pos == npos )
		{
			pos = 0;
			protcol = _RBT("HTTP");
		}
		else
		{
			protcol = string( url.begin() , url.begin()+ pos );
		}

		code = MappingProtocol(protcol,m_protocol);

		if(code != R_SUCCEED)
			return code;

		if(pos != 0)
			pos += 3;

		pos_t posServer = url.find(_RBT("/"),pos);

		if(posServer == npos)
			posServer = url.size();

		pos_t posPort   = url.find_first_of(_RBT(":"));

		if(posPort < pos)
			posPort = npos;

		if(posPort == npos)
		{
			if( m_protocol == HTTP )
				m_port = 80;
			else if( m_protocol == HTTPS )
				m_port = 443;
			posPort = posServer;
		}
		else
		{
			string port( value.begin() + posPort + 1 , value.begin() + posServer );

			code = Convert::ToUInt32( port , m_port );

			if( code != R_SUCCEED )
				return E_URL_PORT;
		}

		m_server = string( value.begin() + pos , value.begin() + posPort );

		pos_t posArgument = url.find(_RBT("?"),posServer);

		if( posArgument == npos )
		{
			m_object = string( value.begin() + posServer , value.end() );
		}
		else
		{
			m_object   = string( value.begin() + posServer , value.begin() + posArgument );
			m_argument = string( value.begin() + posArgument + 1 , value.end() );
		}

		return R_SUCCEED;

	}

public:

	int32 SetEncoding( Encoding* encoding )
	{
		if(encoding == 0)
			return E_URL_ENCODING;
		m_encoding = encoding;
		return R_SUCCEED;
	}

	int32 Encode( cstring& value , string& encode )
	{
		int32 code = R_SUCCEED;

		cache bytes;

		code = Encoding::GetBytes(value,bytes,m_encoding);

		if(code != R_SUCCEED)
			return code;

		code = Convert::ToString( bytes , encode );

		return code;
	}


	int32 Initalize( cstring& url , Encoding* encoding = 0 )
	{
		int32 code = R_SUCCEED;
		
		if( encoding == 0 )
			code = SetEncoding( Encoding::UTF8() );
		else
			code = SetEncoding( encoding );

		if( code != R_SUCCEED )
			return code;

		return ParseUrl(url);
	}

	template< class T >
	int32 Add(cstring& key , const T& value )
	{
		string valueString;

		int32 code = R_SUCCEED;

		code = Convert::ToString( value , valueString );

		if(code != R_SUCCEED )
			return code;

		return Add(key,valueString,false);
	}

	int32 Add( cstring& key , ctchar* value , bool encode )
	{
		return Add( key , string(value) , encode );
	}

	int32 Add( cstring& key , cstring& value , bool encode = true )
	{
		string keyValue;
		if( encode )
		{
			int32 code = R_SUCCEED;

			code = Encode( value , keyValue );

			if( code != R_SUCCEED )
				return code;
		}
		else
		{
			keyValue = value;
		}

		if( !m_argument.empty() && m_argument[m_argument.size() - 1] != _RBT('&') )
			m_argument.append(_RBT("&"));
		m_argument.append(key);
		m_argument.append(_RBT("="));
		m_argument.append(keyValue);

		return R_SUCCEED;
	}

	int32 GetString( string& value )
	{
		if( m_protocol == HTTP )
			value.append(_RBT("http://"));
		else if( m_protocol == HTTPS )
			value.append(_RBT("https://"));
		else
			return E_URL_PROTOCOL;

		value.append(m_server);

		int32 code = R_SUCCEED;
		
		if( 
			(m_protocol == HTTP  && m_port != 80 ) ||
			(m_protocol == HTTPS && m_port != 443 )
			)
		{
			string port;
			code = Convert::ToString( m_port , port );
			if(code != R_SUCCEED)
				return E_URL_PORT;
			value.append(_RBT(":"));
			value.append(port);
		}

		if( !m_object.empty() )
		{
			value.append(m_object);
		}

		if( !m_argument.empty() )
		{
			value.append(_RBT("?"));
			value.append(m_argument);
		}

		return R_SUCCEED;
	}

	int32 GetURL( HTTP_URL& url )
	{
		if( m_encoding == 0 )
			return E_URL_INVALID;
		url = *this;
		return R_SUCCEED;
	}
};


#endif